﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;

//可变参数模版
/*C++11⽀持可变参数模板，也就是说⽀持可变数量参数的函数模板和类模板，可变数⽬的参数被称
为参数包，存在两种参数包：模板参数包，表⽰零或多个模板参数；函数参数包：表⽰零或多个函
数参数。*/

//下面这三种形式
// 函数参数包可以⽤左值引⽤或右值引⽤表⽰，跟普通模板⼀样，每个参数实例化时遵循引⽤折叠规则。
//template <class ...Args> void Func(Args... args) {}
//template <class ...Args> void Func(Args&... args) {}  
//template <class ...Args> void Func(Args&&... args) {}

//template<class... Args>
//void Print(Args&& ... args)
//{
//	cout << "参数个数:" << sizeof...(args) << endl;//可以通过这样来计算参数包的参数个数
//
//	// 不支持这样的语法  因为args不是容器 不能这样使用
////	/*for (size_t i = 0; i < sizeof...(args); i++)
////	{
////		cout << args[i] << " ";
////	}
//}

// 原理1：编译本质这里会结合引用折叠规则实例化出以下四个函数
//void Print();
//void Print(int&& arg1);
//void Print(int&& arg1, string&& arg2);
//void Print(double&& arg1, string&& arg2, double& arg3);
//
// 原理2：更本质去看没有可变参数模板，我们实现出这样的多个函数模板才能支持
// 这里的功能，有了可变参数模板，我们进一步被解放，他是类型泛化基础
// 上叠加数量变化，让我们泛型编程更灵活。
//void Print();
//template <class T1>
//void Print(T1&& arg1);
//template <class T1, class T2>
//void Print(T1&& arg1, T2&& arg2);
//template <class T1, class T2, class T3>
//void Print(T1&& arg1, T2&& arg2, T3&& arg3);


//int main()
//{
//	double x = 2.2;
//	Print(); // 包里有0个参数
//	Print(1); // 包里有1个参数
//	Print(1, string("xxxxx")); // 包里有2个参数
//	Print(1, string("xxxxx"), x); // 包里有3个参数
//
//	return 0;
//}


//对于可变参数包 是在编译时去递归推导解析其参数的 不是在运行时，是在编译时!!!
void ShowList()
{
	// 编译器时递归的终止条件，参数包是0个时，直接匹配这个函数
	cout << endl;
}

template <class T, class ...Args>
void ShowList(T x, Args... args)
{
	cout << x << " ";
	// args是N个参数的参数包
	// 调用ShowList，参数包的第一个传给x，剩下N-1传给第二个参数包
	ShowList(args...); //不断的递归调用 直到参数包数量为0匹配终止的那个函数
}

// 编译时递归推导解析参数
//template <class ...Args>
//void Print(Args... args)  //输出了所有参数
//{
//	ShowList(args...);
//}

//int main()
//{
//	double x = 2.2;
//	Print(); // 包里有0个参数
//	Print(1); // 包里有1个参数
//	Print(1, string("xxxxx")); // 包里有2个参数
//	Print(1, string("xxxxx"), x); // 包里有3个参数
//
//	return 0;
//}

//另一种打印所有参数的方法
template <class T>
int GetArg(const T& x)
{
	cout << x << " ";
	return 0;
}

template <class ...Args>
void Arguments(Args... args)
{}

template <class ...Args>
void Print(Args... args)
{
	// 注意GetArg必须返回或者到的对象，这样才能组成参数包给Arguments
	// 参数包每个参数传给GetArg处理，处理以后返回值作为实参组合参数包传递给Arguments函数
	Arguments(GetArg(args)...);

	cout << endl;
}

// 本质可以理解为编译器编译时，包的扩展模式
// 将上面的函数模板扩展实例化为下面的函数
//void Print(int x, string y, double z)
//{
// Arguments(GetArg(x), GetArg(y), GetArg(z));
//}

int main()
{
	double x = 2.2;
	Print(); // 包里有0个参数
	Print(1); // 包里有1个参数
	Print(1, string("xxxxx")); // 包里有2个参数
	Print(1, string("xxxxx"), x); // 包里有3个参数

	return 0;
}

